import java.util.Arrays;

public class J20250408 {

    //数组名 = 引用变量， 元素 = 对象


    //假设现在要存5个学⽣的javaSE考试成绩，并对其进⾏输出，按照之前掌握的知识点，我们会写出如下代码：
    public static void main1(String[] args) {
        int score1 = 68;
        int score2 = 75;
        int score3 = 60;
        int score4 = 90;

        System.out.println(score1);
        System.out.println(score2);
        System.out.println(score3);
        System.out.println(score4);
    }
    //以上代码没有任何问题，但是如果我们要打印100甚至更多的成绩呢？此时我们就需要引入数组。


//数组的创建

    // T[] 数组名 = new T[N];

    // T：表⽰数组中存放元素的类型
    // T[]：表⽰数组的类型
    // N：表⽰数组的⻓度
    // 数组名存的是元素的地址 称为引用变量 ，元素称为对象，可以说成某某引用指向了某某对象

    public static void main2(String[] args) {
        int[] array1 = new int[10];
        double[] array2 = new double[20];
        String[] array3 = new String[5];      //创建一个可以容纳5个字符串元素的数组，默认值是null
    }


//数组初始化

    public static void main3(String[] args) {

        //动态初始化

        //在创建数组时，直接指定数组中元素的个数
        int[] array = new int[5]; //int类型数组中没有取值的元素为0、 boolean为false、 float为0.0f、 double为0.0
        array[2] = 99;            //赋值
        int ret = array[2];       //将第三个值存储到ret

        int[] array1;             //分为两步来写
        array1 = new int[10];


        //静态初始化

        //格式： T[] 数组名称 = {data1, data2, data3, ..., data n};
        int[] array2 = {1, 2, 3, 4, 5};          //省略了new int[] 一种简写的方式

        int[] array3 = new int[]{1, 2, 3, 4, 5}; //完整写法

        int array4[] = {1, 2, 3, 4, 5};          //按照C语言的写法,容易造成数组的类型就是int的误解,不推荐,合法但不合理

        int[] array5;                            //分为两步来写
        array5 = new int[]{10, 20, 30};
        //【注意事项】
        //静态初始化虽然没有指定数组的⻓度，编译器在编译时会根据{}中元素个数来确定数组的⻓度。
        //静态初始化时,{}中数据类型必须与[]前数据类型⼀致。
        //静态初始化可以简写，省去后⾯的newT[]。
    }


//数组中元素访问

    //数组在内存中是⼀段连续的空间，空间的编号都是从0开始的，依次递增，该编号称为数组的下标，数
    //组可以通过下标访问其任意位置的元素。⽐如：
    public static void main4(String[] args) {
        int[] array = new int[]{10, 20, 30};
        System.out.println(array[0]);        //打印第一个元素
        System.out.println(array[1]);        //打印第二个元素
        System.out.println(array[2]);        //打印第三个元素

        array[2] = 66;                 //可以对数组中的元素进行修改
        System.out.println(array[2]);  // 66

        System.out.println(array[5]);  //超出数组空间报错

        //1.数组是⼀段连续的内存空间，因此⽀持随机访问，即通过下标访问快速访问数组中任意位置的元素
        //2.下标从0开始，介于[0,N）之间不包含N，N为元素个数，不能越界，否则会报出下标越界异常。
    }


//遍历数组

    //所谓"遍历"是指将数组中的所有元素都访问⼀遍,访问是指对数组中的元素进⾏某种操作，⽐如：循环打印。
    public static void main5(String[] args) {
        int[] array = new int[]{10, 20, 30};
        for (int i = 0; i < 3; i++) {           //注意这里不要写成i<=3 否则会超出数组空间
            System.out.print(array[i] + " ");
        }

        //如果数组中增加了⼀个元素，就需要增加⼀条打印语句
        //能否获取到数组的⻓度呢？

        //方法1
        // 注意：在数组中可以通过数组对象.length 来获取数组的⻓度
        int len = array.length;                  //求数组的长度
        System.out.println("数组长度为：" + len);

        for (int i = 0; i < array.length; i++) { //遍历数组打印元素
            System.out.print(array[i] + " ");
        }

        System.out.println();                    //为了美观的换行操作


        //方法2
        //可以使⽤for-each遍历数组
        //没有下标的概念，遍历数组取一个数打印一个数，无法对具体的某下标的一个数进行操作，这就是和上面这个方法的区别。
        //for-each是for循环的另外⼀种使用方式。能够更方便的完成对数组的遍历。可以避免循环条件和更新语句写错
        for (int i : array) {                   //for (数组类型 随便取名 ： 数组名)
            System.out.println(i + " ");
        }


        //方法3
        //借助Java中的工具 Arrays.toString(数组名)，会以字符串的形式返回，注意！只能打印一维数组

        String ret = Arrays.toString(array);        //完整写法，接收、存储
        System.out.println(ret);                    //以字符串的形式返回

        System.out.println(Arrays.toString(array)); //简写，一行代码即可打印

    }


//数组是引⽤类型
    //基本数据类型创建的变量，称为基本变量，该变量空间中直接存放的是其所对应的值；
    //⽽引⽤数据类型创建的变量，⼀般称为对象的引⽤，其空间中存储的是对象所在空间的地址。

    //数组存的是地址
    public static void main6(String[] args) {
        int a = 10;
        int b = 20;
        int array[] = new int[]{1, 2, 3};
        System.out.println(array);      //[I@4eec7777
    }
    //在上述代码中，a、b、arr，都是函数内部的变量，因此其空间都在main⽅法对应的栈帧中分配。
    //a、b是内置类型的变量，因此其空间中保存的就是给该变量初始化的值。
    // array是数组类型的引⽤变量，其内部保存的内容可以简单理解成是数组在堆空间中的⾸地址。
    //引⽤变量并不直接存储对象本⾝，可以简单理解成存储的是对象在堆中空间的起始地址。
    // 通过该地址，引⽤变量便可以去操作对象。有点类似C语⾔中的指针，但是Java中引⽤要⽐指针的操作更简单。


    //引用变量
    public static void main7(String[] args) {
        func();
        System.out.println();//运用方法func，打印数组array
    }

    public static void func() {
        int[] array = new int[3];
        array[0] = 10;
        array[1] = 20;
        array[2] = 30;

        int[] array1 = new int[]{1, 2, 3, 4, 5};
        array1[0] = 100;
        array1[1] = 200;

        array = array1;      //array这个引用指向了array1这个引用所指的对象，JVM自动回收array的对象
        array[2] = 300;
        array[3] = 400;
        array[4] = 500;
        for (int i = 0; i < array1.length; i++) {
            System.out.println(array1[i]);
        }
    }


    //认识null
    public static void main8(String[] args) {
        // null在Java中表示"空引用"，也就是⼀个不指向对象的引用
        // null的作用类似于C语言中的NULL(空指针)，都是表示⼀个无效的内存位置。
        // 因此不能对这个内存进行任何读写操作。⼀旦尝试读写，就会抛出NullPointerException。
        // 注意：Java中并没有约定null和0号地址的内存有任何关联。
        int[] arr = null;
        System.out.println(arr[0]);
    }


//数组的应用场景

    //保存数据

    public static void main9(String[] args) {
        int[] arr = {1, 2, 3};
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }


    //作为函数的参数

    //1.参数传基本数据类型

    public static void main10(String[] args) {
        int num = 0;
        func1(num);
        System.out.println("num = " + num);
    }

    public static void func1(int x) {
        x = 10;
        System.out.println("x = " + x);
    }
    //x = 10
    //num = 0
    //在fun⽅法中修改形参x的值,不影响实参的num值


    //2.参数传数组类型(引⽤数据类型)

    public static void main11(String[] args) {
        int[] arr = {1, 2, 3};
        func2(arr);
        System.out.println("arr[0] = " + arr[0]);
    }

    public static void func2(int[] a) {
        a[0] = 10;
        System.out.println("a[0] = " + a[0]);
    }
    //a[0] = 10
    //arr[0] = 10
    //发现在func⽅法内部修改数组的内容,⽅法外部的数组内容也发⽣改变.因为数组是引⽤类型，按照引⽤类型来进⾏传递，是可以修改其中存放的内容的。
    //总结:
    // 所谓的"引⽤"本质上只是存了⼀个地址.Java将数组设定成引⽤类型,
    //这样的话后续进⾏数组参数传参,其实只是将数组的地址传⼊到函数形参中.
    //这样可以避免对整个数组的拷⻉(数组可能⽐较⻓,那么拷⻉开销就会很⼤).


    //传引用不一定能修改数组中的值，需要看使用引用是修改了形参引用的指向还是通过形参引用修改实参引用所指对象的值

    //1.改变
    public static void teat1(int[] array) {         //地址指向实参数组
        array[0] = 99;                              //将形参99的值赋值到数组中下标为0的数组空间里
    }

    public static void main12(String[] args) {
        int[] array = {1, 2, 3, 4, 5};                  //实参的值
        teat1(array);
        System.out.println(Arrays.toString(array)); //[99,2,3,4,5]
    }

    //2.不改变
    public static void teat2(int[] array2) {          //地址指向实参array2数组的地址，此时形参array2的地址为实参array2的地址
        array2 = new int[]{10, 20, 30, 40, 50};       //开出一块新的栈空间，并将新地址存到新空间形参array2中，
    }                                                 //当这行代码走完，新空间就会被回收，直接执行打印实参array2的代码

    public static void main13(String[] args) {
        int[] array2 = {1, 2, 3, 4, 5};               //数组array2数组的实参
        teat2(array2);                                //执行方法test2
        System.out.println(Arrays.toString(array2));  //[1,2,3,4,5]
    }


    //数组作为参数进行传递

    public static void swap(int[] array) {
        int tem = array[0];
        array[0] = array[1];
        array[1] = tem;
    }

    public static void main15(String[] args) {
        int[] array = {1, 2};
        System.out.println(array[0] + " " + array[1]);// 1 2
        swap(array);
        System.out.println(array[0] + " " + array[1]);// 2 1
    }


    //作为函数的返回值

    public static int add(int a, int b) {
        return a + b;
    }

    public static int[] func2() {
        int a = 1;
        int b = 2;
        int[] ret = new int[]{a, b};
        return ret;
    }

    public static int[] func3() {
        return new int[]{1, 2};
    }

    public static void main16(String[] args) {
        System.out.println(Arrays.toString(func2()));
        System.out.println(Arrays.toString(func3()));
    }


    //获取斐波那契数列的前N项

    public static int[] fib(int n) {
        if (n <= 0) {
            return null;
        }

        int[] array = new int[n];
        array[0] = array[1] = 1;
        for (int i = 2; i < n; i++) {
            array[i] = array[i - 1] + array[i - 2];
        }
        return array;
    }

    public static void main17(String[] args) {
        int[] array = fib(10);
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + " ");
        }
    }




//操作数据⼯具类Arrays与数组练习

    //数组转字符串

    public static void main18(String[] args) {
        int[] arr = {1, 2, 3, 4, 5, 6};

        String newArr = Arrays.toString(arr);
        System.out.println(newArr);
    }

    //数组拷⻉

    public static void main19(String[] args) {
        int[] array1 = {1,2,3,4,5};

        int[] array2 = new int[array1.length];
        System.out.println("拷贝前：");
        System.out.println("array1:" + Arrays.toString(array1));
        System.out.println("array2:" + Arrays.toString(array2));

        for(int i = 0; i < array1.length; i++){
            array2[i] = array1[i];
        }
        System.out.println("拷贝后：");
        System.out.println("array1:" + Arrays.toString(array1));
        System.out.println("array2:" + Arrays.toString(array2));
    }



    //用方法来拷贝数组

    public static int[] copyArray(int[] array){    //定义一个拷贝数组的方法
        int[] copy = new int[array.length];        //定义一个数组copy长度与array一样

        for(int i = 0; i < array.length; i++){     //循环赋值与array相同的元素给数组copy
            copy[i] = array[i];
        }
        return copy;
    }
    public static void main20(String[] args) {
        int[] array = {1,2,3,4,5};
        int[] array2 = copyArray(array);                           //array2接收方法执行返回的数组
        System.out.println("array: " + Arrays.toString(array));    //array: [1, 2, 3, 4, 5]
        System.out.println("array2: " + Arrays.toString(array2));  //array2: [1, 2, 3, 4, 5]
    }



    //使用Java里的便捷拷贝

    public static void main21(String[] args) {
        int[] array1 = {1,2,3,4,5};

        //顺序拷贝
        int[] newArray = Arrays.copyOf(array1, 3);             //数组类型[] 拷贝数组名 = Arrays.copyOf(原数组名, 拷贝数组的长度)
        System.arraycopy(array1,0,newArray,0,3);    //使用System。arraycopy（原数组名,起始下标,拷贝数组名,结束下标,数组长度）
        System.out.println("array1: " + Arrays.toString(array1));        //array1：[1,2,3,4,5]
        System.out.println("newArray:" + Arrays.toString(newArray));     //newArray: [1,2,3]

        //局部拷贝
        int[] array2 = Arrays.copyOfRange(array1,2,4);          //数组类型[] 拷贝数组名 = Array.copyRange(原数组名,开始拷贝的对象下标,结束拷贝的对象下标)   注意这里的下标取值是左闭右开
        System.out.println("array2: " + Arrays.toString(array2));        //array2: [3, 4]

        //多样化拷贝
        int[] array3 = new int[array1.length];                            //定义拷贝的数组长度
        System.arraycopy(array1,2,array3,1,3);       //System.arraycopy(原数组名,原数组开始拷贝的下标,拷贝数组名,在拷贝数组中开始存放数组的位置,拷贝了对象的长度)
        System.out.println("array3: " + Arrays.toString(array3));         //array3: [0, 3, 4, 5, 0]


    }




//数序查找
    //特点：数组必须是有序的

    public static int find(int[] array,int key){
        for(int i = 0; i < array.length; i++){
            if(array[i] == key){
                return i;
            }
        }
        return -1;
    }
    public static void main22(String[] args) {
        int[] array = {1,2,3,4,5};
        int ret = find(array,4);
        if(ret == -1){
            System.out.println("没有你要找的值.....");
        }else{
            System.out.println("你要找的值为: " + ret);
        }
    }

    //二分查找(查找的数组必须要是有序的)

    public static int count = 0;                         //计查找次数
    public static int binarySearch(int[] array,int key){
        int i = 0;                                       //数组左边的下标
        int j = array.length-1;                          //数组右边的下标

        while (i <= j) {
            count++;
            int mid = (i+j)/2;                           //数组中间对象的下标
            if(array[mid] < key){                        //循环查找
                i = mid + 1;
            }else if(array[mid] > key){
                j = mid - 1;
            }else {
                return mid;
            }
        }
        return -1;
    }
    public static void main23(String[] args) {
        int[] array = {1,2,3,4,5};                        //初始化数组

        int ret = binarySearch(array,3);             //使用定义的方法
        if(ret == -1){                                    //循环
            System.out.println("没有你要找的值.....");
        }else{
            System.out.println("你要找的值位置在: " + ret);
            System.out.println("查找次数为" + count);
        }
    }


    //使用Java里面定义的二分查找类方法 Array.binarySearch(数组名,要找的值)

    public static void main24(String[] args) {
        int[] array ={1,3,5,6,8};                         //数组初始化(数组要是有序的)
        int ret = Arrays.binarySearch(array,3);      //将找到的位置下标赋值给ret
        System.out.println("你所查找的值位置在: " + ret);
    }


    //将数组有序化可以使用Arrays.sort(数组名);

    public static void main25(String[] args) {
        int[] array = new int[]{5,4,3,2,1};
        System.out.println("原数组为： " + Arrays.toString(array));      //原数组为：[5,4,3,2,1]
        Arrays.sort(array);                                            //使用Java中的方法使数组有序化
        System.out.println("排序后的数组为： " + Arrays.toString(array)); //序后的数组为：[1,2,3,4,5]
    }



    //冒泡排序(给定⼀个数组,让数组升序/降序排序)

    //升序排法

    public static void bubbleSort(int[] array){
        for(int i = 0;i < array.length-1; i++) {
            boolean flg = false;                          //若某一轮内层循环中未发生任何交换(fLg保持false)说明数组已完全有序
            for (int j = 0; j < array.length-1-i; j++) {  //每一次比较都比上一次少
                if (array[j] > array[j+1]) {              //比较大小并且交换值
                    int tmp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = tmp;
                    flg = true;
                }
            }
            if (!flg){                                    //直接通过break终止外层循环,减少不必要的遍历。
                break;
            }
        }
    }
    public static void main26(String[] args) {
        int[] array = new int[]{2,6,5,1,7,3,8};
        bubbleSort(array);
        System.out.println("排序后的数组: " + Arrays.toString(array));
    }



    //数组逆序

    public static void reverse(int[] array){
        int i = 0;
        int j = array.length-1;
        while(i < j){
            int tmp = array[j];
            array[j] = array[i];
            array[i] = tmp;
            i++;
            j--;
        }
    }
    public static void main27(String[] args) {
        int[] array = new int[]{2,5,3,7,9,8};
        reverse(array);
        System.out.println(Arrays.toString(array));
    }




    //规则二维数组

    public static void main28(String[] args) {
        int[][] array = {{1,2,3},{4,5,6}};           //初始化二维数组

        System.out.println(array[1][1]);             //打印第二行第二列的值 5
        System.out.println(array.length);            //打印数组的维数，也就是行数 2
        System.out.println(array[0]);                //一维数组的地址，也就是第一行的数组地址[I@4dd8dc3
        System.out.println(array[0].length);         //一维数组的列数 3

        //第一种遍历数组打印

        for(int x = 0; x < array.length; x++){
            for(int y = 0; y < array[x].length; y++){
                System.out.print(array[x][y]  + " ");
            }
            System.out.println(" ");
        }

        //第二种遍历打印数组(for each遍历)

        for(int[] a:array){
            for(int x:a ){
                System.out.print(x + " ");
            }
            System.out.println();                    //换行
        }

        //第三种遍历打印数组(用Arrays.deepToString打印)

        System.out.println(Arrays.deepToString(array)); //[[1,2,3],[4,5,6]]

        //第四种遍历数组打印

        for (int x = 0; x < array.length; x++) {
            for (int y = 0; y < array[x].length; y++) {
                System.out.printf("%d\t", array[x][y]);
            }
            System.out.println("");
        }
    }



    //不规则数组

    public static void main29(String[] args) {
        int[][] array = new int[2][];             //定义行数未定义列数

        array[0] = new int[3];                    //令第一行数组有3个元素
        array[1] = new int[5];                    //令第二行数组有5个元素

        for(int x=0; x < array.length; x++){
            for(int y=0; y < array[x].length; y++){
                System.out.print(array[x][y] + " ");
            }
            System.out.println();                 //打印结果为第一行3个0，第二行5个0，未赋值的元素都为0
        }
    }




    //比较两个一维数组的对应元素是否相同使用 Arrays.equals(数组1,数组2)

    public static void main30(String[] args) {
        int[] array1 = {1,2,3};
        int[] array2 = {1,2,5};

        boolean flg = Arrays.equals(array1,array2);    //将布尔类型的结果赋值给flg
        System.out.println(flg);                       //false
    }



    //将一维数组的一定范围的值变为期望值的方法用 Arrays.fill(数组名,起始下标,末尾下标,要变成的值)

    public static void main31(String[] args) {
        int[] array = {1,2,3,4,5,6};

        Arrays.fill(array,1,3,0);  //范围同样是左闭右开
        System.out.println(Arrays.toString(array));     //[1,0,0,4,5,6]
    }
}
